/**
 * Render and Load the bsp scene
 */

bsp_vertex = '\
	#ifdef GL_ES \n\
	precision highp float; \n\
	#endif \n\
	attribute vec3 position; \n\
	attribute vec2 texCoord; \n\
	attribute vec2 lightCoord; \n\
	attribute vec3 normal; \n\
	attribute vec4 color; \n\
	\n\
	varying vec2 vTexCoord; \n\
	varying vec2 vLightmapCoord; \n\
	\n\
	uniform mat4 view; \n\
	uniform mat4 projection; \n\
	\n\
	void main(void) { \n\
		vec4 worldPosition = view * vec4(position, 1.0); \n\
		vTexCoord = texCoord; \n\
		vLightmapCoord = lightCoord; \n\
		gl_Position = projection * worldPosition; \n\
	} \n\
	';

bsp_fragment = '\
	#ifdef GL_ES \n\
	precision highp float; \n\
	#endif \n\
	varying vec2 vTexCoord; \n\
	varying vec2 vLightmapCoord; \n\
	uniform sampler2D texture; \n\
	uniform sampler2D lightmap; \n\
	\n\
	void main(void) { \n\
		vec4 diffuseColor = texture2D(texture, vTexCoord); \n\
		gl_FragColor = vec4(diffuseColor.rgb, diffuseColor.a); \n\
	} \n\
	';


var bsp_attribs  = [ 'position', 'texCoord', 'lightCoord', 'normal', 'color' ];
var bsp_uniforms = [ 'view', 'projection', 'time', 'texture', 'lightmap'];


var source_file = 'data';

var scene_stride = 56;

slBspScene = function( gl )
{	
	// attach gl
	this.gl = gl;
	
	var bsp = this;
	this.worker = new Worker("js/BspWorker.js");
	this.worker.onmessage = function( event )
	{
		//deal with the worker msg
		bsp.onMessage( event );
	};
	
	this.worker.onerror = function( event )
	{
		throw 'Line:' + event.lineno + ',' + event.message;
	};
	
	//buffer data
	this.vertexBuffer 	= null;
	this.indexBuffer 	= null;
	this.indexCount		= 0;
	
	// surfaces
	this.surfaces		= null;
	
	//
	this.highlighted	= null;
	
	// different queue
	this.unShaderSurfaces		= new Array();
	this.defaultShaderSurfaces 	= new Array();
	this.modelShaderSurfaces	= new Array();
	this.effectShaderSurfaces	= new Array();
	
	// the shader from the source
	this.shaders				= new Array();
};

// set the surfaces 
slBspScene.prototype.setSurfaces = function( surfaces )
{
	this.surfaces = surfaces;
};

slBspScene.prototype.onMessage = function( event )
{
	// deal with all the message
	switch( event.data.type )
	{	
		case 'entities':
			{
				this.buildEntity( event.data.entities );
			}
			break;
		case 'geometry':
			{
				this.buildBuffer( event.data.vertices , event.data.indices );
				this.setSurfaces( event.data.surfaces );
				this.makeShaders();
			}
			break;
		case 'lightmap':
			{
				//alert('lightmap');
			}
			break;
		case 'shaders':
			{
				// parse the shader source 
				//alert("shaders");
			}
			break;
		case 'visibility':
			{
				//alert("visibility");
			}
			break;
			
		case 'bsp':
			{
				//alert('bsp');
			}
		break;
		default:
			break;
	}
};

slBspScene.prototype.load = function( url )
{	
	this.worker.postMessage({
        type: 'load',
        url: source_file + '/maps/' + url
    });

};

slBspScene.prototype.loadShaders = function( source )
{
	var map = this;
	for( var i = 0; i < source.length; ++i )
	{
		source[i] = source_file + '/scripts/' + source[i];
	}
	
	slMaterial.loadList( source, function( shaders )
			{
				map.buildShaders(shaders);
			});
};

slBspScene.prototype.buildEntity = function( entity )
{
	// divide and input different queue
	
			
};

// input buffer and index 
slBspScene.prototype.buildBuffer = function( vertices, indices )
{
	var gl = this.gl;
	this.vertexBuffer = gl.createBuffer();
	gl.bindBuffer( gl.ARRAY_BUFFER, this.vertexBuffer );
	gl.bufferData( gl.ARRAY_BUFFER, new Float32Array( vertices ), gl.STATIC_DRAW );
	
	this.indexBuffer = gl.createBuffer();
	gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer );
	gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, new Uint16Array( indices ), gl.STATIC_DRAW );
	
	this.indexCount = indices.length;
};

// apply all the surface data, translate them to the shade data and sort later
slBspScene.prototype.makeShaders = function()
{
	if( this.surfaces == null )
		return;
		
	for(var i = 0; i < this.surfaces.length; ++i )
	{
		var face = this.surfaces[i];
		if(	face.shader || face.elementCount == 0 || face.shaderName == 'textures\\unnamed' )
			continue;
		this.unShaderSurfaces.push( face );	
	}
	
	var scene = this;
	
	// do not know why they used the interal' method
	var interval = setInterval( function(){
		
		if( scene.unShaderSurfaces.length == 0 )
		{
			scene.effectShaderSurfaces.sort( function( a, b )
					{
						var order = a.shader.sort - b.shader.sort;
						return order;
					});
			
			clearInterval( interval );
			return;
		}
		
		var unface = scene.unShaderSurfaces.shift();
		// check the shader whether is created
		var shader = scene.shaders[ unface.shaderName ];
		if( shader )
		{
			unface.shader = shader;
			scene.effectShaderSurfaces.push( unface );
			slShader.buildSourcShader( scene.gl, unface, shader ); 
		}	
		else
		{
			unface.shader = slShader.buildDefaultShader( scene.gl, unface );
			if( unface.geomType == 3 )
			{
				unface.shader.model = true;
				scene.modelShaderSurfaces.push( unface );
			}
			else
			{
				scene.defaultShaderSurfaces.push( unface );
			}			
		}
	}, 15 );
	
	//this.shader = slShader.defaultShader;
};

slBspScene.prototype.buildShaders = function(shaders)
{
	var gl = this.gl;
	for( var i = 0; i < shaders.length; ++i )
	{
		var shader = shaders[i];
		var glShader = slMaterial.build( gl, shader );
		this.shaders[shader.name] = glShader;
	}
};

slBspScene.prototype.bindShaderAttribs = function( program, viewMat, projectMat )
{
	var gl = this.gl;
	
	// setup the view and projection
	gl.uniformMatrix4fv( program.uniform.view, false, slMatrixAsFloat32Array(viewMat) );
	gl.uniformMatrix4fv( program.uniform.projection, false, slMatrixAsFloat32Array(projectMat) );
	
	gl.enableVertexAttribArray( program.attrib.position );
	gl.vertexAttribPointer( program.attrib.position, 4, gl.FLOAT, false, scene_stride, 0 );
	
	if( program.attrib.texCoord != -1 )
	{
		gl.enableVertexAttribArray( program.attrib.texCoord );
		gl.vertexAttribPointer( program.attrib.texCoord, 2, gl.FLOAT, false, scene_stride, 3*4 );
	}
	
	if( program.attrib.lightCoord != -1 )
	{
		gl.enableVertexAttribArray( program.attrib.lightCoord );
		gl.vertexAttribPointer( program.attrib.lightCoord, 2, gl.FLOAT, false, scene_stride, 5*4 );
	}
	if( program.attrib.normal != -1 )
	{
		gl.enableVertexAttribArray( program.attrib.normal );
		gl.vertexAttribPointer( program.attrib.normal, 3, gl.FLOAT, false, scene_stride, 7*4 );
	}
	
	if( program.attrib.color != -1 )
	{
		gl.enableVertexAttribArray( program.attrib.color );
		gl.vertexAttribPointer( program.attrib.color, 4, gl.FLOAT, false, scene_stride, 10*4 );
	}
	
};

slBspScene.prototype.draw = function( context )
{
	if( this.vertexBuffer == null || this.indexBuffer == null )
		return;

	var gl = context.gl;

	var time = context.elapsedTime / 1000;
	
	var view_mat = context.view;
	var projection_mat = context.projection;
	
	if( this.surfaces.length > 0 )
	{
		
		gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer );
		gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer );
			
		if( this.defaultShaderSurfaces.length > 0 || this.unShaderSurfaces.length > 0 )
		{
			var shader = slShader.defaultShader;
			slShader.setShader( gl, shader );
			var shaderProgram = slShader.setShaderStage(gl, shader, shader.stages[0], time );
			this.bindShaderAttribs( shaderProgram, view_mat, projection_mat );
			
			gl.activeTexture( gl.TEXTURE0 );
			gl.bindTexture( gl.TEXTRUE_2D, slShader.defaultTexture );
			
			for( var i = 0; i < this.unShaderSurfaces.length; ++i )
			{
				var surface = this.unShaderSurfaces[i];
				gl.drawElements( gl.TRIANGLES, surface.elementCount, gl.UNSIGNED_SHORT, surface.indexOffset );
			}
			
			for( var i = 0; i < this.defaultShaderSurfaces.length; ++i )
			{
				var surface = this.defaultShaderSurfaces[i];
				var stage = surface.shader.stages[0];
				if( this.highlighted && this.highlighted == surface.shaderName )
				{
					gl.bindTexture( gl.TEXTURE_2D, slShader.defaultTexture );
				}
				else
				{
					gl.bindTexture( gl.TEXTURE_2D, stage.texture );
				}
				gl.drawElements( gl.TRIANGLES, surface.elementCount, gl.UNSIGNED_SHORT, surface.indexOffset );
			}
		}
		
		if( this.modelShaderSurfaces.length > 0 )
		{
			var shader = this.modelShaderSurfaces[0].shader;
			slShader.setShader(gl, shader);
			var program = slShader.setShaderStage(gl, shader, shader.stages[0], time);
			this.bindShaderAttribs(program, view_mat, projection_mat );
			
			gl.activeTexture( gl.TEXTURE0 );
			for( var i = 0; i < this.modelShaderSurfaces.length; ++i )
			{
				var surface = this.modelShaderSurfaces[i];
				var stage = surface.shader.stages[0];
				
				if( this.highlighted && this.highlighted == surface.shaderName )
				{
					gl.bindTexture(gl.TEXTURE_2D, slShader.defaultTexture );
				}
				else
				{
					gl.bindTexture( gl.TEXTURE_2D, stage.texture );
				}
				gl.drawElements( gl.TRIANGLES, surface.elementCount, gl.UNSIGNED_SHORT, surface.indexOffset );
			}
		}
		
		for( var i = 0; i < this.effectShaderSurfaces.length; ++i )
		{
			var surface = this.effectShaderSurfaces[i];
			if( surface.elementCount == 0 || surface.visible != true )
			{
				continue;
			}
			
			var shader = surface.shader;
			
			if( this.highlighted && this.highlighted == surface.shaderName )
			{
				shader = slShader.defaultShader;
			}
			
			if( !slShader.setShader( gl, shader ) )
			{
				continue;
			}
			
			for( var j = 0; j < shader.stages.length; ++j )
			{
				var stage = shader.stages[j];
				
				var program = slShader.setShaderStage(gl, shader, stage, time);
				
				if( !program ){ continue; }
				
				this.bindShaderAttribs(program, view_mat, projection_mat );
				
				gl.drawElements( gl.TRIANGLES, surface.elementCount, gl.UNSIGNED_SHORT, surface.indexOffset );
			}
		}
	}
	
};

